package com.base.springboot.starter.common.cipher.impl;

import com.base.springboot.starter.common.cipher.CipherException;
import com.base.springboot.starter.common.cipher.SymmetryCryptedMachine;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Security;
import java.util.Arrays;

/**
 * 提供解密操作的密码机，基于AES对称加密算法和boucycassle算法提供者
 * User: wangwei
 * Date: 2021-04-22 2:03 下午
 */
@Slf4j
public class AesBcCryptedMachine implements SymmetryCryptedMachine {

    /** 算法实现第三方 boucycassle */
    public static final String BC = "BC";

    /** 秘钥对齐长度 */
    private static int keyLengthMod = 16;

    /** 对称算法 */
    public static final String AES_CBC_PKCS_7_PADDING = "AES/CBC/PKCS7Padding";

    public static final String AES = "AES";

    private static Cipher encripteCipher = null;

    static {
        Security.addProvider(new BouncyCastleProvider());
        try {
            encripteCipher = Cipher.getInstance(AES_CBC_PKCS_7_PADDING, BC);
        } catch (Exception e) {
            log.error("fail to init Cipher", e);
        }
    }

    @Override
    //@CatAop
    public String decrypt(String encryptedData, String iv, String sessionKey) throws CipherException {

        log.info("decrypt param ===> encryptedData:{},iv:{},sessionKey:{}", encryptedData, iv, sessionKey);
        Preconditions.checkArgument(!Strings.isNullOrEmpty(encryptedData));
        Preconditions.checkArgument(!Strings.isNullOrEmpty(iv));
        Preconditions.checkArgument(!Strings.isNullOrEmpty(sessionKey));

        try {
            // 被加密的数据
            byte[] dataByte = Base64.decode(encryptedData);
            // 加密秘钥
            byte[] keyByte = Base64.decode(sessionKey);
            SecretKeySpec spec = new SecretKeySpec(fixKeyByte(keyByte), AES);
            // 偏移量
            byte[] ivByte = Base64.decode(iv);
            AlgorithmParameters parameters = AlgorithmParameters.getInstance(AES);
            parameters.init(new IvParameterSpec(ivByte));

            byte[] descryptByte = descrypt0(dataByte, spec, parameters);
            return new String(descryptByte, Charsets.UTF_8);
        } catch (Exception e) {
            log.error("", e);
            throw new CipherException(e);
        }
    }

    //@CatAop
    private synchronized byte[] descrypt0(byte[] dataByte, SecretKeySpec spec, AlgorithmParameters parameters)
            throws InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        encripteCipher.init(Cipher.DECRYPT_MODE, spec, parameters);
        return encripteCipher.doFinal(dataByte);
    }

    /**
     * 秘钥长度对齐
     * @param sourceKeyByte
     * @return
     */
    private byte[] fixKeyByte(byte[] sourceKeyByte) {
        if (sourceKeyByte.length % keyLengthMod != 0) {
            int groups = sourceKeyByte.length / keyLengthMod + (sourceKeyByte.length % keyLengthMod != 0 ? 1 : 0);
            byte[] baseLengthKeyByte = new byte[groups * keyLengthMod];
            Arrays.fill(baseLengthKeyByte, (byte) 0);
            System.arraycopy(sourceKeyByte, 0, baseLengthKeyByte, 0, sourceKeyByte.length);
            return baseLengthKeyByte;
        }
        return sourceKeyByte;
    }

    @Override
    public void init() {
    }
}
